<script setup lang="ts">
import type { DialogType } from '@fluentui/web-components';

import { onMounted, ref, watch } from 'vue';

/**
 * Props for the Dialog component
 */
interface DialogProps {
  /**
   * The ID of the element that describes the dialog
   */
  ariaDescribedby?: string;

  /**
   * The ID of the element that labels the dialog
   */
  ariaLabelledby?: string;

  /**
   * The type of dialog
   * - modal: Blocks interaction with the page until dismissed
   * - non-modal: Allows interaction with the page while open
   * - alert: Requires explicit user interaction to dismiss
   */
  type?: DialogType;

  /**
   * Controls the visibility of the dialog (v-model binding)
   */
  modelValue?: boolean;

  /**
   * Prevents the dialog from being closed by clicking outside or pressing ESC
   */
  preventClose?: boolean;
}

const props = withDefaults(defineProps<DialogProps>(), {
  ariaDescribedby: undefined,
  ariaLabelledby: undefined,
  type: 'modal',
  modelValue: false,
  preventClose: false,
});

/**
 * Emitted events
 */
const emit = defineEmits<{
  /**
   * Emitted before the dialog's open state changes
   */
  beforetoggle: [event: CustomEvent];

  /**
   * Emitted after the dialog's open state changes
   */
  toggle: [event: CustomEvent];

  /**
   * Update the model value (for v-model binding)
   */
  'update:modelValue': [value: boolean];
}>();

// Reference to the dialog element
const dialogRef = ref<HTMLElement | null>(null);

// Track internal dialog state
const isOpen = ref(false);

/**
 * Show the dialog
 */
const show = () => {
  if (dialogRef.value) {
    (dialogRef.value as any).show();
    emit('update:modelValue', true);
  }
};

/**
 * Hide the dialog
 */
const hide = () => {
  if (dialogRef.value) {
    (dialogRef.value as any).hide();
    emit('update:modelValue', false);
  }
};

/**
 * Handle clicking on the dialog backdrop for light-dismiss
 */
const clickHandler = (event: Event) => {
  if (
    dialogRef.value &&
    event.target === dialogRef.value &&
    props.type !== 'alert' &&
    !props.preventClose
  ) {
    hide();
  }
};

/**
 * Handle keyboard events to prevent ESC key from closing dialog
 */
const keydownHandler = (event: KeyboardEvent) => {
  if (event.key === 'Escape' && props.preventClose) {
    event.preventDefault();
    event.stopPropagation();
  }
};

// Watch for modelValue changes from parent
watch(
  () => props.modelValue,
  (newValue) => {
    if (newValue && !isOpen.value) {
      show();
    } else if (!newValue && isOpen.value) {
      hide();
    }
  },
  { immediate: true },
);

// Handle toggle events to keep local state in sync
onMounted(() => {
  if (dialogRef.value) {
    dialogRef.value.addEventListener('toggle', (e: Event) => {
      const customEvent = e as CustomEvent;
      isOpen.value = customEvent.detail.newState === 'open';
      emit('update:modelValue', isOpen.value);
    });

    // Set initial state
    if (props.modelValue) {
      show();
    }
  }
});

// Expose methods to parent
defineExpose({
  show,
  hide,
  dialog: dialogRef,
});
function onBeforetoggle(e: CustomEvent) {
  if (props.preventClose) {
    e.preventDefault();
    e.stopPropagation();
  } else {
    emit('beforetoggle', e);
  }
}
function toggle(e: CustomEvent) {
  emit('toggle', e);
  isOpen.value = e.detail.newState === 'open';
  emit('update:modelValue', isOpen.value);
}
</script>

<template>
  <fluent-dialog
    :aria-describedby="ariaDescribedby"
    :aria-labelledby="ariaLabelledby"
    :type="preventClose ? 'alert' : type"
    ref="dialogRef"
    @beforetoggle="onBeforetoggle"
    @toggle="toggle"
    @click="clickHandler"
    @keydown="keydownHandler"
  >
    <slot></slot>
  </fluent-dialog>
</template>
